﻿//===============================================================================
// Jammer.NET Development Library
// Jmr.Security
//===============================================================================
// Copyright © Junaid Fakhruddin, Matthew Leedham and Randolph Cabral.  
// All rights reserved.
//===============================================================================
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================

using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using Jmr.Entity.Poco.Silverlight;

namespace Jmr.Security
{
	/// <summary>
	/// SecurityPrincipalLoadedEventHandler delegate.
	/// </summary>
	/// <param name="principal">A reference to the SecurityPrincipal instance.</param>
	public delegate void SecurityPrincipalLoadedEventHandler(SecurityPrincipal principal);

	/// <summary>
	/// SecurityPrincipal class.  Represents a User in the security database.
	/// </summary>
	public class SecurityPrincipal
	{
		private User _user = null;
		private User _userIdentityImpersonated = null;
		private IEntitySessionProxy _session = null;
		private List<UserRoleView> _userRoleColl = new List<UserRoleView>();
		private List<Application> _applicationColl = new List<Application>();
		private bool _rolesLoaded = false;
		private bool _applicationsLoaded = false;
		private bool _userLoaded = false;
		private string _uri = string.Empty;
		private string _assfull = string.Empty;
		private string _mappingfull = string.Empty;

		/// <summary>
		/// OnSecurityPrincipalLoaded fires when the current instance is loaded.
		/// </summary>
		public event SecurityPrincipalLoadedEventHandler OnSecurityPrincipalLoaded;

		/// <summary>
		/// Loads the current instance from the database.
		/// </summary>
		/// <param name="identity">The identity instance to load.</param>
		/// <param name="uri">Uri of the service end point.</param>
		/// <param name="assyfullname">Full assembly name of the mapping class.</param>
		/// <param name="mappingfullname">Full name of the mapping class.</param>
		public void Initialize(IIdentity identity, string uri, string assyfullname, string mappingfullname)
		{
			_uri = uri;
			_assfull = assyfullname;
			_mappingfull = mappingfullname;
			_session = EntitySessionFactory.CreateSession(uri, assyfullname, mappingfullname);
			_session.OnFetchSingleCompleted -= new FetchSingleCompletedEventHandler(User_OnFetchSingleCompleted);
			_session.OnFetchSingleCompleted += new FetchSingleCompletedEventHandler(User_OnFetchSingleCompleted);
			var id = ParseNtLogon(identity.Name);
      _session.FetchSingle<User>(new User { Domain = id[0], NtLogon = id[1] });
		}

		/// <summary>
		/// Loads the current instance from the database.
		/// </summary>
		/// <param name="ntLogon">The NT logon to load.</param>
		/// <param name="uri">Uri of the service end point.</param>
		/// <param name="assyfullname">Full assembly name of the mapping class.</param>
		/// <param name="mappingfullname">Full name of the mapping class.</param>
		public void Initialize(string ntLogon, string uri, string assyfullname, string mappingfullname)
		{
			_uri = uri;
			_assfull = assyfullname;
			_mappingfull = mappingfullname;
			_session = EntitySessionFactory.CreateSession(uri, assyfullname, mappingfullname);
			_session.OnFetchSingleCompleted -= new FetchSingleCompletedEventHandler(User_OnFetchSingleCompleted);
			_session.OnFetchSingleCompleted += new FetchSingleCompletedEventHandler(User_OnFetchSingleCompleted);
      var id = ParseNtLogon(ntLogon);
      _session.FetchSingle<User>(new User { Domain = id[0], NtLogon = id[1] });
		}

		private void User_OnFetchSingleCompleted(object sender, FetchSingleEventArgs e)
		{
			_user = (User)e.Result;
			_userLoaded = true;
			//if (OnSecurityPrincipalLoaded != null)
			//	OnSecurityPrincipalLoaded(this);

			//Something's wrong here.  Getting "NotFound" errors.
			FillUserRolesColl(_user.Id);
			FillApplicationsColl(_user.Id);
		}

		private void FillUserRolesColl(int id)
		{
			if (!(_user.IsActive)) { return; }

			_session = EntitySessionFactory.CreateSession(_uri, _assfull, _mappingfull);
			_session.OnFetchSetCompleted -= new FetchSetCompletedEventHandler(_session_OnFetchSetCompleted);
			_session.OnFetchSetCompleted += new FetchSetCompletedEventHandler(_session_OnFetchSetCompleted);

			_session.FetchSet<UserRoleView>(new UserRoleViewArgs { UserId = id });
		}

		private void FillApplicationsColl(int id)
		{
			if (!(_user.IsActive)) { return; }

			_session = EntitySessionFactory.CreateSession(_uri, _assfull, _mappingfull);
			_session.OnFetchSetCompleted -= new FetchSetCompletedEventHandler(_session_OnApplicationFetchSetCompleted);
			_session.OnFetchSetCompleted += new FetchSetCompletedEventHandler(_session_OnApplicationFetchSetCompleted);

			_session.FetchSet<Application>(new User { Id = id });
		}


		void _session_OnApplicationFetchSetCompleted(object sender, FetchSetEventArgs e)
		{
			if (e.Results is List<Application>)
			{
				_applicationColl = (List<Application>)e.Results;
				_applicationsLoaded = true;
			}
			if (IsInitialized)
			{
				if (OnSecurityPrincipalLoaded != null)
					OnSecurityPrincipalLoaded(this);

				_rolesLoaded = false;
				_applicationsLoaded = false;
			}
		}

		void _session_OnFetchSetCompleted(object sender, FetchSetEventArgs e)
		{
			if (e.Results is List<UserRoleView>)
			{
				_userRoleColl = (List<UserRoleView>)e.Results;
				_rolesLoaded = true;
			}

			if (IsInitialized)
			{
				if (OnSecurityPrincipalLoaded != null)
					OnSecurityPrincipalLoaded(this);

				_rolesLoaded = false;
				_applicationsLoaded = false;
			}
		}

		private bool IsInitialized
		{
			get
			{
				return _rolesLoaded && _applicationsLoaded;
			}
		}

		/// <summary>
		/// Represents the logged on current user.
		/// </summary>
		public User User
		{
			get
			{
				if (_userLoaded)
					return _user;

				return null;
			}
		}

		private void AddUserRolesColl(SecurityPrincipal principal)
		{
			if (!(_user.IsActive)) { return; }
			foreach (UserRoleView role in principal.UserRoleColl)
			{
				role.IsImpersonated = true;
				UserRoleColl.Add(role);
			}

		}

		private void AddApplicationsColl(SecurityPrincipal principal)
		{
			if (!(_user.IsActive)) { return; }

			foreach (Application app in principal.ApplicationsColl)
			{
				app.IsImpersonated = true;
				ApplicationsColl.Add(app);
			}
		}

		private void ReplaceUserRolesColl(SecurityPrincipal principal)
		{
			if (!(_user.IsActive)) { return; }
			UserRoleColl.Clear();
			foreach (UserRoleView role in principal.UserRoleColl)
			{
				role.IsImpersonated = true;
				UserRoleColl.Add(role);
			}
		}

		private void ReplaceApplicationsColl(SecurityPrincipal principal)
		{
			if (!(_user.IsActive)) { return; }
			ApplicationsColl.Clear();
			foreach (Application app in principal.ApplicationsColl)
			{
				app.IsImpersonated = true;
				ApplicationsColl.Add(app);
			}
		}

		/// <summary>
		/// Indicates if the current user is impersonating any roles.
		/// </summary>
		public bool IsImpersonating
		{
			get { return (_userIdentityImpersonated != null); }
		}

		/// <summary>
		/// Represents the impersonated user.
		/// </summary>
		public User JmrIdentityImpersonated
		{
			get 
			{
					return _userIdentityImpersonated;
			}
		}

		/// <summary>
		/// Replaces the current user's roles with another user's roles.
		/// </summary>
		/// <param name="impersonatingPrincipal">Principal to impersonate.</param>
		public void Impersonate(SecurityPrincipal impersonatingPrincipal)
		{
			Impersonate(impersonatingPrincipal, false);
		}

		/// <summary>
		/// Adds or replaces the current user's roles with another user's roles.
		/// </summary>
		/// <param name="impersonatingPrincipal">Principal to impersonate.</param>
		/// <param name="keepExistingRoles">When true, will keep the current user's roles.</param>
		public void Impersonate(SecurityPrincipal impersonatingPrincipal, bool keepExistingRoles)
		{
			_userIdentityImpersonated = impersonatingPrincipal.User;
			if (keepExistingRoles)
			{
				AddUserRolesColl(impersonatingPrincipal);
				AddApplicationsColl(impersonatingPrincipal);
			}
			else
			{
				ReplaceUserRolesColl(impersonatingPrincipal);
				ReplaceApplicationsColl(impersonatingPrincipal);
			}
		}

		/// <summary>
		/// Restores the current user's roles.
		/// </summary>
		public void RestoreIdentity()
		{
			_userIdentityImpersonated = null;
			FillUserRolesColl(_user.Id);
			FillApplicationsColl(_user.Id);
		}

		/// <summary>
		/// Determines if the current principal has a specific role.
		/// </summary>
		/// <param name="role">Role to find.</param>
		/// <returns>Returns true or false.</returns>
		public bool IsInRole(string role)
		{
			bool ret = false;
			if (UserRoleColl.Where(e=> e.RoleName == role).Count() > 0)
				ret = true;
			return ret;
		}

		/// <summary>
		/// Contains all of the roles for the current principal.
		/// </summary>
		public List<UserRoleView> UserRoleColl 
		{
			get { return _userRoleColl; 	}
			set { _userRoleColl = value; }
		}

		/// <summary>
		/// Contains all of the applications for the current principal.
		/// </summary>
		public List<Application> ApplicationsColl 
		{
			get	{	return _applicationColl; }
			set { _applicationColl = value; }
		}

    private string[] ParseNtLogon(string ntLogon)
    {
      //should we handle local machine logins?
      return ntLogon.Split(new char[] { '\\' });
    }
	}
}
